﻿using Chaotic.Module.DBTool.Views;
using Microsoft.SqlServer.TransactSql.ScriptDom;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Chaotic.Module.DBTool.Common
{
    public class SqlParserManager
    {
        public static SqlParserManager Instance { get; } = new SqlParserManager();
        TSql170Parser _sql170Parser;

        private SqlParserManager() 
        {
            _sql170Parser = new TSql170Parser(true);
        }

        /// <summary>
        /// 拆分sql语句
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        public bool SplitSqlStatements(string sql,out List<string> strings,out List<string> execStrings)
        {
            strings = new List<string>();
            execStrings = new List<string>();
            if (string.IsNullOrWhiteSpace(sql))
                return false;

            sql = sql.Replace("\r\n", " ").Replace("\n", " ").Replace("\r", " ");
            var fragment = _sql170Parser.Parse(new StringReader(sql), out IList<ParseError> errors);
            if (errors.Count > 0)
            {
                List<string> sqls = sql.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries).ToList();
                if (sqls.Count == 0)
                {
                    sqls.Add(sql);
                }
                execStrings.Add(sql);

                foreach (var s in sqls)
                {
                    string str = s.Trim();
                    if (str.StartsWith("insert", StringComparison.CurrentCultureIgnoreCase) ||
                        str.StartsWith("update", StringComparison.CurrentCultureIgnoreCase) ||
                        str.StartsWith("delete", StringComparison.CurrentCultureIgnoreCase))
                    {
                        execStrings.Add(str);
                    }
                    else
                    {
                        strings.Add(str);
                    }
                }
            }
            else
            {
                if (fragment != null)
                {
                    if (fragment is TSqlScript sqlScript)
                    {
                        // 遍历 Batches，一个脚本可以包含多个 Batches（通常由 GO 语句分隔）
                        foreach (TSqlBatch batch in sqlScript.Batches)
                        {
                            // 遍历每个 Batch 中的语句
                            foreach (TSqlStatement statement in batch.Statements)
                            {
                                // 提取语句的原始文本
                                string statementText = sql.Substring(statement.StartOffset, statement.FragmentLength).TrimStart();
                                if (statementText.StartsWith("insert", StringComparison.CurrentCultureIgnoreCase) ||
                                    statementText.StartsWith("update", StringComparison.CurrentCultureIgnoreCase) ||
                                    statementText.StartsWith("delete", StringComparison.CurrentCultureIgnoreCase))
                                {
                                    execStrings.Add(statementText);
                                }
                                else
                                {
                                    strings.Add(statementText);
                                }
                            }
                        }
                    }
                }
            }
            return true;
        }


        public List<TableReferenceInfo> ParseTablesWithAliases(string sql, out IList<TSqlParserToken> sqlParserTokens)
        {
            sqlParserTokens = null;
            if (string.IsNullOrEmpty(sql))
                return new List<TableReferenceInfo>(); 

            var fragment = _sql170Parser.Parse(new StringReader(sql), out IList<ParseError> errors);

            if (fragment == null || fragment.ScriptTokenStream == null)
                return new List<TableReferenceInfo>();

            sqlParserTokens = fragment.ScriptTokenStream;

            if (errors == null || errors.Count == 0)
            {
                var tableVisitor = new TableAliasVisitor();
                fragment.Accept(tableVisitor);
                return tableVisitor.TableAliases;
            }
            else
            {
                bool isTable = false;
                bool isAlias = false;
                List<TableReferenceInfo> list = new List<TableReferenceInfo>();
                foreach (var token in fragment.ScriptTokenStream)
                {
                    if (token.TokenType == TSqlTokenType.WhiteSpace ||
                        token.TokenType == TSqlTokenType.As ||
                        token.TokenType == TSqlTokenType.On)
                        continue;

                    if (token.TokenType == TSqlTokenType.From ||
                        token.TokenType == TSqlTokenType.Join)
                    {
                        isTable = true;
                    }
                    else if (token.TokenType == TSqlTokenType.Identifier)
                    {
                        if (isTable)
                        {
                            isTable = false;
                            isAlias = true;
                            list.Add(new TableReferenceInfo()
                            {
                                TableName = token.Text
                            });
                        }
                        else
                        {
                            if (isAlias && list.Count > 0)
                            {
                                list.Last().Alias = token.Text;
                                isAlias = false;
                            }
                        }
                    }
                    else
                    {
                        isTable = false;
                        isAlias = false;
                    }
                }

                return list;
            }
        }
    }
}
